package com.geezdata.cps.base.validator;

import java.lang.reflect.InvocationTargetException;
import java.util.Set;

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;

import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.beanutils.PropertyUtils;

import com.geezdata.cps.base.annotation.ValidateIfAnotherFieldHasValue;

/*
  @ValidateIfAnotherFieldHasValue(
            fieldName = "userType",
            fieldValue = "2",
            dependFieldName = "property2")
  public class ComplexUserForm {
       int userType;

       @Valid
       UserPropertyForm property1;
       UserPropertyForm property2;
 */
public class ValidateIfAnotherFieldHasValueValidator
	implements ConstraintValidator<ValidateIfAnotherFieldHasValue, Object> {
	
	private String fieldName;
	private String expectedFieldValue;
	private String dependFieldName;
	
	@Override
	public void initialize(final ValidateIfAnotherFieldHasValue annotation) {
	    fieldName          = annotation.fieldName();
	    expectedFieldValue = annotation.fieldValue();
	    dependFieldName    = annotation.dependFieldName();
	}
	
	@Override
	public boolean isValid(final Object value, final ConstraintValidatorContext ctx) {
	
	    if (value == null) {
	        return true;
	    }
	
	    try {
	        final String fieldValue       = BeanUtils.getProperty(value, fieldName);
	        final Object dependFieldValue = PropertyUtils.getProperty(value, dependFieldName);
	
	        if (expectedFieldValue.equals(fieldValue)) {
	
	            ctx.disableDefaultConstraintViolation();
	            ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
	            Validator validator = factory.getValidator();
	
	            Set<ConstraintViolation<Object>> errorList = validator.validate(dependFieldValue);
	
	            for(ConstraintViolation<Object> error : errorList) {
	
	                 ctx.buildConstraintViolationWithTemplate(error.getMessageTemplate())
	                 .addPropertyNode(dependFieldName+"."+error.getPropertyPath())
	                 .addConstraintViolation();
	            }
	
	            return errorList.isEmpty();
	        }
	
	    } catch (final NoSuchMethodException ex) {
	        throw new RuntimeException(ex);
	
	    } catch (final InvocationTargetException ex) {
	        throw new RuntimeException(ex);
	
	    } catch (final IllegalAccessException ex) {
	        throw new RuntimeException(ex);
	    }
	
	    return true;
	}

}